Introduction

OVCAR4A and 4B both showed a number of mitochondrial pathways that were downregulated. This notebook looks into the genes driving those pathways’ regulation. Of note, neither OVCAR4A nor 4B had any upregulated mitochondrial pathways.

Inputs

Inputs consisted of

Read in metadata table

sensitive_resistant_pairs <- c("OVCAR3A_vs_OVCAR3", "OVCAR3B_vs_OVCAR3", "OVCAR4A_vs_OVCAR4", "OVCAR4B_vs_OVCAR4", "PEA2_vs_PEA1", "PEO6_vs_PEO1", "PEO4_vs_PEO1")

isolines <- data.frame(pair = sensitive_resistant_pairs,
                       isoline = c("OVCAR3",
                                   "OVCAR3",
                                   "OVCAR4",
                                   "OVCAR4",
                                   "PEA",
                                   "PEO",
                                   "PEO"))
metadata.all <- as.data.frame(read.table("deseq/metadata.csv", sep = ",", header = TRUE))
rownames(metadata.all) <- metadata.all$ShortName

# Should put this in the metadata file, but just doing this to save time
for (row in 1:nrow(metadata.all)) {
    isogenicRank <- 1
    resistant <- 0
    if (metadata.all$CellLine[row] %in% list("OVCAR3A", "OVCAR4A", "PEA2", "PEO4")) {
      isogenicRank <- 2
      resistant <- 1
    } else if (metadata.all$CellLine[row] %in% list("OVCAR3B", "OVCAR4B", "PEO6")) {
      isogenicRank <- 3
      resistant <- 1
    }
    metadata.all$IsogenicRank[row] <- isogenicRank
    metadata.all$Resistant[row] <- resistant
}
metadata.all

Load TPM matrix

TPM <- as.matrix(read.delim("../star_salmon/salmon.merged.gene_tpm.tsv", sep="\t", row.names="gene_id"))
TPM <- TPM[,-1]
TPM <- matrix(as.numeric(TPM), ncol = ncol(TPM), dimnames = list(rownames(TPM), colnames(TPM)))
TPM.log <- log(TPM + 1)
colnames(TPM.log) <- metadata.all$ShortName
as.data.frame(TPM.log)

OVCAR4 downregulated mitochondrial pathways

Pulling the genes related to mitochondrial pathways regulated in OVCAR4.

# Get pathways consistently regulated in either OVCAR4A or OVCAR4B related to mitochondria
pathways_file = "deseq/output/differential_pathways_all_sensitive_resistant_pairs.csv"
pathways = as.data.frame(read.csv(pathways_file, sep = ",", header = TRUE, row.names = 1))

pathways$OVCAR4A_sig_reg = pathways$OVCAR4A_vs_OVCAR4_padj %>%
  map_lgl(\(padj) !is.na(padj) && padj < 0.05)
pathways$OVCAR4B_sig_reg = pathways$OVCAR4B_vs_OVCAR4_padj %>%
  map_lgl(\(padj) !is.na(padj) && padj < 0.05)
OVCAR4_pathways = pathways[pathways$OVCAR4A_sig_reg == TRUE | pathways$OVCAR4B_sig_reg, c("Description", "OVCAR4A_vs_OVCAR4_padj", "OVCAR4A_vs_OVCAR4_NES", "OVCAR4B_vs_OVCAR4_padj", "OVCAR4B_vs_OVCAR4_NES")]

print(OVCAR4_pathways)

# Save OVCAR4 pathways so that it can be annotated for pathways related to the mitochondria
OVCAR4_pathways_formatted = OVCAR4_pathways
OVCAR4_pathways_formatted$GO_id = rownames(OVCAR4_pathways_formatted)
OVCAR4_pathways_formatted$Mitochondrial = NA
OVCAR4_pathways_formatted = OVCAR4_pathways_formatted[, c("GO_id", "Description", "Mitochondrial")]
write.csv(OVCAR4_pathways_formatted, file = "deseq/output/OVCAR4_sig_regulated_mitochondrial_pathways.csv")

Mitochondrial pathway annotation

Kristin hand-annotated the pathways regulated in either 4A or 4B as to whether or not each pathway is related to mitochondria. This is saved as OVCAR4_sig_regulated_mitochondrial_pathways_annotated.csv Note: This list of relevant mitochondrial pathways would need to be updated if the regulated pathways changes.

Mitochondrial genes of interest

Genes of interest are the genes that – for any significantly regulated pathway in OVCAR4A or OVCAR4B – were part of the core enrichment genes for that pathway and had a differential expression padj < 0.005. (Low padj value chosen to get down to a reasonable number of genes to view plots of.) Core enrichment genes are the most upregulated genes in upregulated pathways or the most downregulated genes in downregulated pathways

# Subset only the pathways that are related to the mitochondria
OVCAR4_mitochondrial_pathways = as.data.frame(read.csv("deseq/specific-pathways/OVCAR4_sig_regulated_mitochondrial_pathways_annotated.csv", sep = ",", header = TRUE, row.names = 1))
OVCAR4_mitochondrial_pathways = OVCAR4_mitochondrial_pathways[OVCAR4_mitochondrial_pathways$Mitochondrial %in% c("Y", "y"),]

get_core_mitochondrial_enrichment_genes = function(cellline_pathways_file, deseq_results_file, name) {
  cellline_pathways = as.data.frame(read.csv(cellline_pathways_file, sep = ",", header = TRUE, row.names = 1))
  cellline_deseq_res = as.data.frame(read.csv(deseq_results_file, sep = ",", header = TRUE, row.names = 1))
  
  cellline_genes_of_interest = c()
  
  for (row in 1:nrow(OVCAR4_mitochondrial_pathways)) {
    id = rownames(OVCAR4_mitochondrial_pathways)[row]
    
    if (id %in% rownames(cellline_pathways)) {
      core_enrichment_genes = str_split(cellline_pathways[rownames(cellline_pathways) == id, "core_enrichment_genes"], "/")[[1]]
      # Because there were 200-300 core enrichment genes related to mitochondrial pathways in OVCAR4 generally, we want to have a stricter definition of our genes of interest
      core_genes_res = cellline_deseq_res[rownames(cellline_deseq_res) %in% core_enrichment_genes, c("log2FoldChange", "padj")]
      genes_of_interest = core_genes_res[!is.na(core_genes_res$padj) & core_genes_res$padj < 0.005,]
      
      cellline_genes_of_interest = c(cellline_genes_of_interest, rownames(genes_of_interest))
    }
  }
  
  if (length(cellline_genes_of_interest) > 0) {
    df = as.data.frame(table(cellline_genes_of_interest))
    colnames(df) = c("gene", name)
    return(df)
  } else {
    return(data.frame(gene = c(),
                      count = c()))
  }
  
}

df_1 = get_core_mitochondrial_enrichment_genes("deseq/output/OVCAR4A_vs_OVCAR4_significantly_downregulated_pathways.csv",
                                          "deseq/output/OVCAR4A_vs_OVCAR4_deseq_results.csv",
                                          "important_in_4A_down_pathways")

df_2 = get_core_mitochondrial_enrichment_genes("deseq/output/OVCAR4B_vs_OVCAR4_significantly_downregulated_pathways.csv",
                                          "deseq/output/OVCAR4B_vs_OVCAR4_deseq_results.csv",
                                          "important_in_4B_down_pathways")

df_3 = get_core_mitochondrial_enrichment_genes("deseq/output/OVCAR4A_vs_OVCAR4_significantly_upregulated_pathways.csv",
                                          "deseq/output/OVCAR4A_vs_OVCAR4_deseq_results.csv",
                                          "important_in_4A_up_pathways")

df_4 = as.data.frame(get_core_mitochondrial_enrichment_genes("deseq/output/OVCAR4B_vs_OVCAR4_significantly_upregulated_pathways.csv",
                                          "deseq/output/OVCAR4B_vs_OVCAR4_deseq_results.csv",
                                          "important_in_4B_up_pathways"))

genes_of_interest = merge(df_1, df_2, by = "gene", all = TRUE)

if (nrow(df_3) != 0) {
  print("Upregulated genes found! Will need to update the titles of the plots")
  genes_of_interest = merge(genes_of_interest, df_3, by = "gene", all = TRUE)
}

if (nrow(df_4) != 0) {
  print("Upregulated genes found! Will need to update the titles of the plots")
  genes_of_interest = merge(genes_of_interest, df_4, by = "gene", all = TRUE)
}

# Replace all NA values with 0 in the entire data frame
genes_of_interest <- replace(genes_of_interest, is.na(genes_of_interest), 0)

print(genes_of_interest)

Plot the genes of interest important for regulated mitochondrial pathways in OVCAR4 resistant lines

Need to access the deseq gene-level data to include padj values per cellline in the plots

# Need Deseq gene data for significance of each gene within a cellline
gene_deseq_file = "deseq/output/differential_gene_expression_all_sensitive_resistant_pairs.csv"

Restarting R session...

Plots based on the transcripts per million data for the genes driving the mitochondrial pathways. Note: Would need to update title if the regulated paths change and there are upregulated mitochondrial paths

TPM.log <- as.data.frame(TPM.log)
TPM.log.genes.interesting <- cbind(metadata.all, as.data.frame(t(TPM.log[rownames(TPM.log) %in% genes_of_interest$gene,])))

# To plot each gene separately:
for(row in 1:nrow(genes_of_interest))
{
  gene = as.character(genes_of_interest[row, "gene"])
  num_4A_down = as.numeric(genes_of_interest[row, "important_in_4A_down_pathways"])
  num_4B_down = as.numeric(genes_of_interest[row, "important_in_4B_down_pathways"])
  
  # title = str_interp("${gene} (# downregulated paths affected: 4A - ${num_4A_down}; 4B - ${num_4B_down}. upreg paths: none)")
  title = gene
  plotData <- TPM.log.genes.interesting[, c("CellLine", "IsoLine", gene)]
  colnames(plotData)[3] <- "log.TPM"
  
  # Collect significance data for each cellline
  sigData <- data.frame(CellLine = unique(plotData$CellLine))
  sigData$significant = sigData$CellLine %>%
    map_lgl(\(cellline) !is.na(padj_gene_data[gene, cellline]) && (padj_gene_data[gene, cellline] < 0.05))
  plotData$significance = rownames(plotData) %>%
    map_chr(\(rowname) {
      cellline = plotData[rowname, "CellLine"]
      significance = sigData[sigData$CellLine == cellline, "significant"]
      label = ifelse(!is.na(significance) && significance, "*", "")
      return(label)
    })
  plotData$sig_height = rownames(plotData) %>% # Label significance 15% higher than highest value
    map_dbl(\(rowname) {
      cellline = plotData[rowname, "CellLine"]
      isoline = plotData[rowname, "IsoLine"]
      rep_values = plotData[plotData$CellLine == cellline, "log.TPM"]
      isoline_values = plotData[plotData$IsoLine == isoline, "log.TPM"]
      height = min(isoline_values) + 1.15 * (max(rep_values) - min(isoline_values))
      return(height)
    })
  
  plotData$label_height = rownames(plotData) %>% # Label name of cellline 10% higher than highest value
    map_dbl(\(rowname) {
      cellline = plotData[rowname, "CellLine"]
      isoline = plotData[rowname, "IsoLine"]
      rep_values = plotData[plotData$CellLine == cellline, "log.TPM"]
      isoline_values = plotData[plotData$IsoLine == isoline, "log.TPM"]
      height = min(isoline_values) + 1.10 * (max(rep_values) - min(isoline_values))
      return(height)
    })
  
  # For setting ylim
  range_values = max(max(plotData$log.TPM), max(plotData$sig_height)) - min(plotData$log.TPM)
  
  print(
    ggplot(plotData, aes(CellLine, log.TPM, color = CellLine,)) +
      geom_beeswarm(alpha = 0.3) +
      geom_boxplot(outlier.shape = NA, fill = NA) +
      geom_text(y = plotData$sig_height, size = 10, label = plotData$significance) +
      geom_text(y = plotData$label_height, size = 2, label = plotData$CellLine) +
      ylim(min(plotData$log.TPM) - 0.1 * range_values, max(plotData$log.TPM) + 0.15 * range_values) + 
      theme_classic() +
      theme(axis.title.x=element_blank(),
            axis.text.x=element_blank(),
            axis.ticks.x=element_blank()) +
      ggtitle(title))
}

LS0tCnRpdGxlOiAiR2VuZXMgcmVndWxhdGluZyBtaXRvY2hvbmRyaWFsIHBhdGh3YXlzIGluIE9WQ0FSIDRBIGFuZCA0QiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMgSW50cm9kdWN0aW9uCgpPVkNBUjRBIGFuZCA0QiBib3RoIHNob3dlZCBhIG51bWJlciBvZiBtaXRvY2hvbmRyaWFsIHBhdGh3YXlzIHRoYXQgd2VyZSBkb3ducmVndWxhdGVkLiBUaGlzIG5vdGVib29rIGxvb2tzIGludG8gdGhlIGdlbmVzIGRyaXZpbmcgdGhvc2UgcGF0aHdheXMnIHJlZ3VsYXRpb24uIE9mIG5vdGUsIG5laXRoZXIgT1ZDQVI0QSBub3IgNEIgaGFkIGFueSB1cHJlZ3VsYXRlZCBtaXRvY2hvbmRyaWFsIHBhdGh3YXlzLgoKIyMgSW5wdXRzCgpJbnB1dHMgY29uc2lzdGVkIG9mCgotICAgTWV0YWRhdGEgc3ByZWFkc2hlZXQgZm9yIERFU2VxMgotICAgc2FsbW9uLm1lcmdlZC5nZW5lX3RwbS50c3YKLSAgIFJlc3VsdHMgZnJvbSBkZXNlcS1hbmFseXNpcy5SbWQKCmBgYHtyIGxvYWQgcGFja2FnZXMsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShERVNlcTIpCmxpYnJhcnkodnNuKQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGJpb21hUnQpCmxpYnJhcnkoREVTZXFBbmFseXNpcykKbGlicmFyeShVcFNldFIpCmxpYnJhcnkoZ3Byb2ZpbGVyMikKbGlicmFyeShycnZnbykKbGlicmFyeShHTy5kYikKbGlicmFyeShnZ2ZvcnRpZnkpCkdPIDwtIGFzLmxpc3QoR09URVJNKQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShlbnJpY2hwbG90KQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeSgib3JnLkhzLmVnLmRiIikKbGlicmFyeShnZ2JlZXN3YXJtKQpsaWJyYXJ5KGdnZm9yY2UpCmBgYAoKIyMjIFJlYWQgaW4gbWV0YWRhdGEgdGFibGUKCmBgYHtyfQpzZW5zaXRpdmVfcmVzaXN0YW50X3BhaXJzIDwtIGMoIk9WQ0FSM0FfdnNfT1ZDQVIzIiwgIk9WQ0FSM0JfdnNfT1ZDQVIzIiwgIk9WQ0FSNEFfdnNfT1ZDQVI0IiwgIk9WQ0FSNEJfdnNfT1ZDQVI0IiwgIlBFQTJfdnNfUEVBMSIsICJQRU82X3ZzX1BFTzEiLCAiUEVPNF92c19QRU8xIikKCmlzb2xpbmVzIDwtIGRhdGEuZnJhbWUocGFpciA9IHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMsCiAgICAgICAgICAgICAgICAgICAgICAgaXNvbGluZSA9IGMoIk9WQ0FSMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9WQ0FSMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9WQ0FSNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9WQ0FSNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBFQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBFTyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBFTyIpKQpgYGAKCmBgYHtyIGxvYWQgbWV0YXRhYmxlfQptZXRhZGF0YS5hbGwgPC0gYXMuZGF0YS5mcmFtZShyZWFkLnRhYmxlKCJkZXNlcS9tZXRhZGF0YS5jc3YiLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUpKQpyb3duYW1lcyhtZXRhZGF0YS5hbGwpIDwtIG1ldGFkYXRhLmFsbCRTaG9ydE5hbWUKCiMgU2hvdWxkIHB1dCB0aGlzIGluIHRoZSBtZXRhZGF0YSBmaWxlLCBidXQganVzdCBkb2luZyB0aGlzIHRvIHNhdmUgdGltZQpmb3IgKHJvdyBpbiAxOm5yb3cobWV0YWRhdGEuYWxsKSkgewogICAgaXNvZ2VuaWNSYW5rIDwtIDEKICAgIHJlc2lzdGFudCA8LSAwCiAgICBpZiAobWV0YWRhdGEuYWxsJENlbGxMaW5lW3Jvd10gJWluJSBsaXN0KCJPVkNBUjNBIiwgIk9WQ0FSNEEiLCAiUEVBMiIsICJQRU80IikpIHsKICAgICAgaXNvZ2VuaWNSYW5rIDwtIDIKICAgICAgcmVzaXN0YW50IDwtIDEKICAgIH0gZWxzZSBpZiAobWV0YWRhdGEuYWxsJENlbGxMaW5lW3Jvd10gJWluJSBsaXN0KCJPVkNBUjNCIiwgIk9WQ0FSNEIiLCAiUEVPNiIpKSB7CiAgICAgIGlzb2dlbmljUmFuayA8LSAzCiAgICAgIHJlc2lzdGFudCA8LSAxCiAgICB9CiAgICBtZXRhZGF0YS5hbGwkSXNvZ2VuaWNSYW5rW3Jvd10gPC0gaXNvZ2VuaWNSYW5rCiAgICBtZXRhZGF0YS5hbGwkUmVzaXN0YW50W3Jvd10gPC0gcmVzaXN0YW50Cn0KbWV0YWRhdGEuYWxsCmBgYAoKIyMjIExvYWQgVFBNIG1hdHJpeApgYGB7cn0KVFBNIDwtIGFzLm1hdHJpeChyZWFkLmRlbGltKCIuLi9zdGFyX3NhbG1vbi9zYWxtb24ubWVyZ2VkLmdlbmVfdHBtLnRzdiIsIHNlcD0iXHQiLCByb3cubmFtZXM9ImdlbmVfaWQiKSkKVFBNIDwtIFRQTVssLTFdClRQTSA8LSBtYXRyaXgoYXMubnVtZXJpYyhUUE0pLCBuY29sID0gbmNvbChUUE0pLCBkaW1uYW1lcyA9IGxpc3Qocm93bmFtZXMoVFBNKSwgY29sbmFtZXMoVFBNKSkpClRQTS5sb2cgPC0gbG9nKFRQTSArIDEpCmNvbG5hbWVzKFRQTS5sb2cpIDwtIG1ldGFkYXRhLmFsbCRTaG9ydE5hbWUKYXMuZGF0YS5mcmFtZShUUE0ubG9nKQpgYGAKCiMjIE9WQ0FSNCBkb3ducmVndWxhdGVkIG1pdG9jaG9uZHJpYWwgcGF0aHdheXMKClB1bGxpbmcgdGhlIGdlbmVzIHJlbGF0ZWQgdG8gbWl0b2Nob25kcmlhbCBwYXRod2F5cyByZWd1bGF0ZWQgaW4gT1ZDQVI0LgoKYGBge3J9CiMgR2V0IHBhdGh3YXlzIGNvbnNpc3RlbnRseSByZWd1bGF0ZWQgaW4gZWl0aGVyIE9WQ0FSNEEgb3IgT1ZDQVI0QiByZWxhdGVkIHRvIG1pdG9jaG9uZHJpYQpwYXRod2F5c19maWxlID0gImRlc2VxL291dHB1dC9kaWZmZXJlbnRpYWxfcGF0aHdheXNfYWxsX3NlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMuY3N2IgpwYXRod2F5cyA9IGFzLmRhdGEuZnJhbWUocmVhZC5jc3YocGF0aHdheXNfZmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSkKCnBhdGh3YXlzJE9WQ0FSNEFfc2lnX3JlZyA9IHBhdGh3YXlzJE9WQ0FSNEFfdnNfT1ZDQVI0X3BhZGogJT4lCiAgbWFwX2xnbChcKHBhZGopICFpcy5uYShwYWRqKSAmJiBwYWRqIDwgMC4wNSkKcGF0aHdheXMkT1ZDQVI0Ql9zaWdfcmVnID0gcGF0aHdheXMkT1ZDQVI0Ql92c19PVkNBUjRfcGFkaiAlPiUKICBtYXBfbGdsKFwocGFkaikgIWlzLm5hKHBhZGopICYmIHBhZGogPCAwLjA1KQpPVkNBUjRfcGF0aHdheXMgPSBwYXRod2F5c1twYXRod2F5cyRPVkNBUjRBX3NpZ19yZWcgPT0gVFJVRSB8IHBhdGh3YXlzJE9WQ0FSNEJfc2lnX3JlZywgYygiRGVzY3JpcHRpb24iLCAiT1ZDQVI0QV92c19PVkNBUjRfcGFkaiIsICJPVkNBUjRBX3ZzX09WQ0FSNF9ORVMiLCAiT1ZDQVI0Ql92c19PVkNBUjRfcGFkaiIsICJPVkNBUjRCX3ZzX09WQ0FSNF9ORVMiKV0KCnByaW50KE9WQ0FSNF9wYXRod2F5cykKCiMgU2F2ZSBPVkNBUjQgcGF0aHdheXMgc28gdGhhdCBpdCBjYW4gYmUgYW5ub3RhdGVkIGZvciBwYXRod2F5cyByZWxhdGVkIHRvIHRoZSBtaXRvY2hvbmRyaWEKT1ZDQVI0X3BhdGh3YXlzX2Zvcm1hdHRlZCA9IE9WQ0FSNF9wYXRod2F5cwpPVkNBUjRfcGF0aHdheXNfZm9ybWF0dGVkJEdPX2lkID0gcm93bmFtZXMoT1ZDQVI0X3BhdGh3YXlzX2Zvcm1hdHRlZCkKT1ZDQVI0X3BhdGh3YXlzX2Zvcm1hdHRlZCRNaXRvY2hvbmRyaWFsID0gTkEKT1ZDQVI0X3BhdGh3YXlzX2Zvcm1hdHRlZCA9IE9WQ0FSNF9wYXRod2F5c19mb3JtYXR0ZWRbLCBjKCJHT19pZCIsICJEZXNjcmlwdGlvbiIsICJNaXRvY2hvbmRyaWFsIildCndyaXRlLmNzdihPVkNBUjRfcGF0aHdheXNfZm9ybWF0dGVkLCBmaWxlID0gImRlc2VxL291dHB1dC9PVkNBUjRfc2lnX3JlZ3VsYXRlZF9taXRvY2hvbmRyaWFsX3BhdGh3YXlzLmNzdiIpCmBgYAojIyMgTWl0b2Nob25kcmlhbCBwYXRod2F5IGFubm90YXRpb24KCktyaXN0aW4gaGFuZC1hbm5vdGF0ZWQgdGhlIHBhdGh3YXlzIHJlZ3VsYXRlZCBpbiBlaXRoZXIgNEEgb3IgNEIgYXMgdG8gd2hldGhlciBvciBub3QgZWFjaCBwYXRod2F5IGlzIHJlbGF0ZWQgdG8gbWl0b2Nob25kcmlhLiBUaGlzIGlzIHNhdmVkIGFzIGBPVkNBUjRfc2lnX3JlZ3VsYXRlZF9taXRvY2hvbmRyaWFsX3BhdGh3YXlzX2Fubm90YXRlZC5jc3ZgCk5vdGU6IFRoaXMgbGlzdCBvZiByZWxldmFudCBtaXRvY2hvbmRyaWFsIHBhdGh3YXlzIHdvdWxkIG5lZWQgdG8gYmUgdXBkYXRlZCBpZiB0aGUgcmVndWxhdGVkIHBhdGh3YXlzIGNoYW5nZXMuCgojIyBNaXRvY2hvbmRyaWFsIGdlbmVzIG9mIGludGVyZXN0CgpHZW5lcyBvZiBpbnRlcmVzdCBhcmUgdGhlIGdlbmVzIHRoYXQgLS0gZm9yIGFueSBzaWduaWZpY2FudGx5IHJlZ3VsYXRlZCBwYXRod2F5IGluIE9WQ0FSNEEgb3IgT1ZDQVI0QiAtLSB3ZXJlIHBhcnQgb2YgdGhlIGNvcmUgZW5yaWNobWVudCBnZW5lcyBmb3IgdGhhdCBwYXRod2F5IGFuZCBoYWQgYSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBwYWRqIDwgMC4wMDUuIChMb3cgcGFkaiB2YWx1ZSBjaG9zZW4gdG8gZ2V0IGRvd24gdG8gYSByZWFzb25hYmxlIG51bWJlciBvZiBnZW5lcyB0byB2aWV3IHBsb3RzIG9mLikgQ29yZSBlbnJpY2htZW50IGdlbmVzIGFyZSB0aGUgbW9zdCB1cHJlZ3VsYXRlZCBnZW5lcyBpbiB1cHJlZ3VsYXRlZCBwYXRod2F5cyBvciB0aGUgbW9zdCBkb3ducmVndWxhdGVkIGdlbmVzIGluIGRvd25yZWd1bGF0ZWQgcGF0aHdheXMKCmBgYHtyfQojIFN1YnNldCBvbmx5IHRoZSBwYXRod2F5cyB0aGF0IGFyZSByZWxhdGVkIHRvIHRoZSBtaXRvY2hvbmRyaWEKT1ZDQVI0X21pdG9jaG9uZHJpYWxfcGF0aHdheXMgPSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KCJkZXNlcS9zcGVjaWZpYy1wYXRod2F5cy9PVkNBUjRfc2lnX3JlZ3VsYXRlZF9taXRvY2hvbmRyaWFsX3BhdGh3YXlzX2Fubm90YXRlZC5jc3YiLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpKQpPVkNBUjRfbWl0b2Nob25kcmlhbF9wYXRod2F5cyA9IE9WQ0FSNF9taXRvY2hvbmRyaWFsX3BhdGh3YXlzW09WQ0FSNF9taXRvY2hvbmRyaWFsX3BhdGh3YXlzJE1pdG9jaG9uZHJpYWwgJWluJSBjKCJZIiwgInkiKSxdCgpnZXRfY29yZV9taXRvY2hvbmRyaWFsX2VucmljaG1lbnRfZ2VuZXMgPSBmdW5jdGlvbihjZWxsbGluZV9wYXRod2F5c19maWxlLCBkZXNlcV9yZXN1bHRzX2ZpbGUsIG5hbWUpIHsKICBjZWxsbGluZV9wYXRod2F5cyA9IGFzLmRhdGEuZnJhbWUocmVhZC5jc3YoY2VsbGxpbmVfcGF0aHdheXNfZmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSkKICBjZWxsbGluZV9kZXNlcV9yZXMgPSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KGRlc2VxX3Jlc3VsdHNfZmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSkKICAKICBjZWxsbGluZV9nZW5lc19vZl9pbnRlcmVzdCA9IGMoKQogIAogIGZvciAocm93IGluIDE6bnJvdyhPVkNBUjRfbWl0b2Nob25kcmlhbF9wYXRod2F5cykpIHsKICAgIGlkID0gcm93bmFtZXMoT1ZDQVI0X21pdG9jaG9uZHJpYWxfcGF0aHdheXMpW3Jvd10KICAgIAogICAgaWYgKGlkICVpbiUgcm93bmFtZXMoY2VsbGxpbmVfcGF0aHdheXMpKSB7CiAgICAgIGNvcmVfZW5yaWNobWVudF9nZW5lcyA9IHN0cl9zcGxpdChjZWxsbGluZV9wYXRod2F5c1tyb3duYW1lcyhjZWxsbGluZV9wYXRod2F5cykgPT0gaWQsICJjb3JlX2VucmljaG1lbnRfZ2VuZXMiXSwgIi8iKVtbMV1dCiAgICAgICMgQmVjYXVzZSB0aGVyZSB3ZXJlIDIwMC0zMDAgY29yZSBlbnJpY2htZW50IGdlbmVzIHJlbGF0ZWQgdG8gbWl0b2Nob25kcmlhbCBwYXRod2F5cyBpbiBPVkNBUjQgZ2VuZXJhbGx5LCB3ZSB3YW50IHRvIGhhdmUgYSBzdHJpY3RlciBkZWZpbml0aW9uIG9mIG91ciBnZW5lcyBvZiBpbnRlcmVzdAogICAgICBjb3JlX2dlbmVzX3JlcyA9IGNlbGxsaW5lX2Rlc2VxX3Jlc1tyb3duYW1lcyhjZWxsbGluZV9kZXNlcV9yZXMpICVpbiUgY29yZV9lbnJpY2htZW50X2dlbmVzLCBjKCJsb2cyRm9sZENoYW5nZSIsICJwYWRqIildCiAgICAgIGdlbmVzX29mX2ludGVyZXN0ID0gY29yZV9nZW5lc19yZXNbIWlzLm5hKGNvcmVfZ2VuZXNfcmVzJHBhZGopICYgY29yZV9nZW5lc19yZXMkcGFkaiA8IDAuMDA1LF0KICAgICAgCiAgICAgIGNlbGxsaW5lX2dlbmVzX29mX2ludGVyZXN0ID0gYyhjZWxsbGluZV9nZW5lc19vZl9pbnRlcmVzdCwgcm93bmFtZXMoZ2VuZXNfb2ZfaW50ZXJlc3QpKQogICAgfQogIH0KICAKICBpZiAobGVuZ3RoKGNlbGxsaW5lX2dlbmVzX29mX2ludGVyZXN0KSA+IDApIHsKICAgIGRmID0gYXMuZGF0YS5mcmFtZSh0YWJsZShjZWxsbGluZV9nZW5lc19vZl9pbnRlcmVzdCkpCiAgICBjb2xuYW1lcyhkZikgPSBjKCJnZW5lIiwgbmFtZSkKICAgIHJldHVybihkZikKICB9IGVsc2UgewogICAgcmV0dXJuKGRhdGEuZnJhbWUoZ2VuZSA9IGMoKSwKICAgICAgICAgICAgICAgICAgICAgIGNvdW50ID0gYygpKSkKICB9CiAgCn0KYGBgCgpgYGB7cn0KCmRmXzEgPSBnZXRfY29yZV9taXRvY2hvbmRyaWFsX2VucmljaG1lbnRfZ2VuZXMoImRlc2VxL291dHB1dC9PVkNBUjRBX3ZzX09WQ0FSNF9zaWduaWZpY2FudGx5X2Rvd25yZWd1bGF0ZWRfcGF0aHdheXMuY3N2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRlc2VxL291dHB1dC9PVkNBUjRBX3ZzX09WQ0FSNF9kZXNlcV9yZXN1bHRzLmNzdiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJpbXBvcnRhbnRfaW5fNEFfZG93bl9wYXRod2F5cyIpCgpkZl8yID0gZ2V0X2NvcmVfbWl0b2Nob25kcmlhbF9lbnJpY2htZW50X2dlbmVzKCJkZXNlcS9vdXRwdXQvT1ZDQVI0Ql92c19PVkNBUjRfc2lnbmlmaWNhbnRseV9kb3ducmVndWxhdGVkX3BhdGh3YXlzLmNzdiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJkZXNlcS9vdXRwdXQvT1ZDQVI0Ql92c19PVkNBUjRfZGVzZXFfcmVzdWx0cy5jc3YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaW1wb3J0YW50X2luXzRCX2Rvd25fcGF0aHdheXMiKQoKZGZfMyA9IGdldF9jb3JlX21pdG9jaG9uZHJpYWxfZW5yaWNobWVudF9nZW5lcygiZGVzZXEvb3V0cHV0L09WQ0FSNEFfdnNfT1ZDQVI0X3NpZ25pZmljYW50bHlfdXByZWd1bGF0ZWRfcGF0aHdheXMuY3N2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRlc2VxL291dHB1dC9PVkNBUjRBX3ZzX09WQ0FSNF9kZXNlcV9yZXN1bHRzLmNzdiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJpbXBvcnRhbnRfaW5fNEFfdXBfcGF0aHdheXMiKQoKZGZfNCA9IGFzLmRhdGEuZnJhbWUoZ2V0X2NvcmVfbWl0b2Nob25kcmlhbF9lbnJpY2htZW50X2dlbmVzKCJkZXNlcS9vdXRwdXQvT1ZDQVI0Ql92c19PVkNBUjRfc2lnbmlmaWNhbnRseV91cHJlZ3VsYXRlZF9wYXRod2F5cy5jc3YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZGVzZXEvb3V0cHV0L09WQ0FSNEJfdnNfT1ZDQVI0X2Rlc2VxX3Jlc3VsdHMuY3N2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImltcG9ydGFudF9pbl80Ql91cF9wYXRod2F5cyIpKQoKZ2VuZXNfb2ZfaW50ZXJlc3QgPSBtZXJnZShkZl8xLCBkZl8yLCBieSA9ICJnZW5lIiwgYWxsID0gVFJVRSkKCmlmIChucm93KGRmXzMpICE9IDApIHsKICBwcmludCgiVXByZWd1bGF0ZWQgZ2VuZXMgZm91bmQhIFdpbGwgbmVlZCB0byB1cGRhdGUgdGhlIHRpdGxlcyBvZiB0aGUgcGxvdHMiKQogIGdlbmVzX29mX2ludGVyZXN0ID0gbWVyZ2UoZ2VuZXNfb2ZfaW50ZXJlc3QsIGRmXzMsIGJ5ID0gImdlbmUiLCBhbGwgPSBUUlVFKQp9CgppZiAobnJvdyhkZl80KSAhPSAwKSB7CiAgcHJpbnQoIlVwcmVndWxhdGVkIGdlbmVzIGZvdW5kISBXaWxsIG5lZWQgdG8gdXBkYXRlIHRoZSB0aXRsZXMgb2YgdGhlIHBsb3RzIikKICBnZW5lc19vZl9pbnRlcmVzdCA9IG1lcmdlKGdlbmVzX29mX2ludGVyZXN0LCBkZl80LCBieSA9ICJnZW5lIiwgYWxsID0gVFJVRSkKfQoKIyBSZXBsYWNlIGFsbCBOQSB2YWx1ZXMgd2l0aCAwIGluIHRoZSBlbnRpcmUgZGF0YSBmcmFtZQpnZW5lc19vZl9pbnRlcmVzdCA8LSByZXBsYWNlKGdlbmVzX29mX2ludGVyZXN0LCBpcy5uYShnZW5lc19vZl9pbnRlcmVzdCksIDApCgpwcmludChnZW5lc19vZl9pbnRlcmVzdCkKCmBgYAoKIyMgUGxvdCB0aGUgZ2VuZXMgb2YgaW50ZXJlc3QgaW1wb3J0YW50IGZvciByZWd1bGF0ZWQgbWl0b2Nob25kcmlhbCBwYXRod2F5cyBpbiBPVkNBUjQgcmVzaXN0YW50IGxpbmVzCgpOZWVkIHRvIGFjY2VzcyB0aGUgZGVzZXEgZ2VuZS1sZXZlbCBkYXRhIHRvIGluY2x1ZGUgcGFkaiB2YWx1ZXMgcGVyIGNlbGxsaW5lIGluIHRoZSBwbG90cwpgYGB7cn0KIyBOZWVkIERlc2VxIGdlbmUgZGF0YSBmb3Igc2lnbmlmaWNhbmNlIG9mIGVhY2ggZ2VuZSB3aXRoaW4gYSBjZWxsbGluZQpnZW5lX2Rlc2VxX2ZpbGUgPSAiZGVzZXEvb3V0cHV0L2RpZmZlcmVudGlhbF9nZW5lX2V4cHJlc3Npb25fYWxsX3NlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMuY3N2IgpnZW5lX2Rlc2VxID0gYXMuZGF0YS5mcmFtZShyZWFkLmNzdihnZW5lX2Rlc2VxX2ZpbGUsIHNlcCA9ICIsIiwgaGVhZGVyID0gVFJVRSwgcm93Lm5hbWVzID0gMSkpCnBhZGpfZ2VuZV9kYXRhIDwtIGdlbmVfZGVzZXEgJT4lCiAgc2VsZWN0KGNvbnRhaW5zKCJwYWRqIikpCgojIFJlZm9ybWF0IGNvbHVtbiBuYW1lcyB0byBqdXN0IHVzZSB0aGUgcmVzaXN0YW50IGNlbGxsaW5lCmNvbG5hbWVzKHBhZGpfZ2VuZV9kYXRhKSA9IGNvbG5hbWVzKHBhZGpfZ2VuZV9kYXRhKSAlPiUKICBtYXBfY2hyKFwoY29sbmFtZSkgc3RyX3NwbGl0KGNvbG5hbWUsICJfIilbWzFdXVtbMV1dW1sxXV0pCgpwcmludChwYWRqX2dlbmVfZGF0YSkKYGBgCgpQbG90cyBiYXNlZCBvbiB0aGUgdHJhbnNjcmlwdHMgcGVyIG1pbGxpb24gZGF0YSBmb3IgdGhlIGdlbmVzIGRyaXZpbmcgdGhlIG1pdG9jaG9uZHJpYWwgcGF0aHdheXMuIE5vdGU6IFdvdWxkIG5lZWQgdG8gdXBkYXRlIHRpdGxlIGlmIHRoZSByZWd1bGF0ZWQgcGF0aHMgY2hhbmdlIGFuZCB0aGVyZSBhcmUgdXByZWd1bGF0ZWQgbWl0b2Nob25kcmlhbCBwYXRocwoKYGBge3J9ClRQTS5sb2cgPC0gYXMuZGF0YS5mcmFtZShUUE0ubG9nKQpUUE0ubG9nLmdlbmVzLmludGVyZXN0aW5nIDwtIGNiaW5kKG1ldGFkYXRhLmFsbCwgYXMuZGF0YS5mcmFtZSh0KFRQTS5sb2dbcm93bmFtZXMoVFBNLmxvZykgJWluJSBnZW5lc19vZl9pbnRlcmVzdCRnZW5lLF0pKSkKCiMgVG8gcGxvdCBlYWNoIGdlbmUgc2VwYXJhdGVseToKZm9yKHJvdyBpbiAxOm5yb3coZ2VuZXNfb2ZfaW50ZXJlc3QpKQp7CiAgZ2VuZSA9IGFzLmNoYXJhY3RlcihnZW5lc19vZl9pbnRlcmVzdFtyb3csICJnZW5lIl0pCiAgbnVtXzRBX2Rvd24gPSBhcy5udW1lcmljKGdlbmVzX29mX2ludGVyZXN0W3JvdywgImltcG9ydGFudF9pbl80QV9kb3duX3BhdGh3YXlzIl0pCiAgbnVtXzRCX2Rvd24gPSBhcy5udW1lcmljKGdlbmVzX29mX2ludGVyZXN0W3JvdywgImltcG9ydGFudF9pbl80Ql9kb3duX3BhdGh3YXlzIl0pCiAgCiAgIyB0aXRsZSA9IHN0cl9pbnRlcnAoIiR7Z2VuZX0gKCMgZG93bnJlZ3VsYXRlZCBwYXRocyBhZmZlY3RlZDogNEEgLSAke251bV80QV9kb3dufTsgNEIgLSAke251bV80Ql9kb3dufS4gdXByZWcgcGF0aHM6IG5vbmUpIikKICB0aXRsZSA9IGdlbmUKICBwbG90RGF0YSA8LSBUUE0ubG9nLmdlbmVzLmludGVyZXN0aW5nWywgYygiQ2VsbExpbmUiLCAiSXNvTGluZSIsIGdlbmUpXQogIGNvbG5hbWVzKHBsb3REYXRhKVszXSA8LSAibG9nLlRQTSIKICAKICAjIENvbGxlY3Qgc2lnbmlmaWNhbmNlIGRhdGEgZm9yIGVhY2ggY2VsbGxpbmUKICBzaWdEYXRhIDwtIGRhdGEuZnJhbWUoQ2VsbExpbmUgPSB1bmlxdWUocGxvdERhdGEkQ2VsbExpbmUpKQogIHNpZ0RhdGEkc2lnbmlmaWNhbnQgPSBzaWdEYXRhJENlbGxMaW5lICU+JQogICAgbWFwX2xnbChcKGNlbGxsaW5lKSAhaXMubmEocGFkal9nZW5lX2RhdGFbZ2VuZSwgY2VsbGxpbmVdKSAmJiAocGFkal9nZW5lX2RhdGFbZ2VuZSwgY2VsbGxpbmVdIDwgMC4wNSkpCiAgcGxvdERhdGEkc2lnbmlmaWNhbmNlID0gcm93bmFtZXMocGxvdERhdGEpICU+JQogICAgbWFwX2NocihcKHJvd25hbWUpIHsKICAgICAgY2VsbGxpbmUgPSBwbG90RGF0YVtyb3duYW1lLCAiQ2VsbExpbmUiXQogICAgICBzaWduaWZpY2FuY2UgPSBzaWdEYXRhW3NpZ0RhdGEkQ2VsbExpbmUgPT0gY2VsbGxpbmUsICJzaWduaWZpY2FudCJdCiAgICAgIGxhYmVsID0gaWZlbHNlKCFpcy5uYShzaWduaWZpY2FuY2UpICYmIHNpZ25pZmljYW5jZSwgIioiLCAiIikKICAgICAgcmV0dXJuKGxhYmVsKQogICAgfSkKICBwbG90RGF0YSRzaWdfaGVpZ2h0ID0gcm93bmFtZXMocGxvdERhdGEpICU+JSAjIExhYmVsIHNpZ25pZmljYW5jZSAxNSUgaGlnaGVyIHRoYW4gaGlnaGVzdCB2YWx1ZQogICAgbWFwX2RibChcKHJvd25hbWUpIHsKICAgICAgY2VsbGxpbmUgPSBwbG90RGF0YVtyb3duYW1lLCAiQ2VsbExpbmUiXQogICAgICBpc29saW5lID0gcGxvdERhdGFbcm93bmFtZSwgIklzb0xpbmUiXQogICAgICByZXBfdmFsdWVzID0gcGxvdERhdGFbcGxvdERhdGEkQ2VsbExpbmUgPT0gY2VsbGxpbmUsICJsb2cuVFBNIl0KICAgICAgaXNvbGluZV92YWx1ZXMgPSBwbG90RGF0YVtwbG90RGF0YSRJc29MaW5lID09IGlzb2xpbmUsICJsb2cuVFBNIl0KICAgICAgaGVpZ2h0ID0gbWluKGlzb2xpbmVfdmFsdWVzKSArIDEuMTUgKiAobWF4KHJlcF92YWx1ZXMpIC0gbWluKGlzb2xpbmVfdmFsdWVzKSkKICAgICAgcmV0dXJuKGhlaWdodCkKICAgIH0pCiAgCiAgcGxvdERhdGEkbGFiZWxfaGVpZ2h0ID0gcm93bmFtZXMocGxvdERhdGEpICU+JSAjIExhYmVsIG5hbWUgb2YgY2VsbGxpbmUgMTAlIGhpZ2hlciB0aGFuIGhpZ2hlc3QgdmFsdWUKICAgIG1hcF9kYmwoXChyb3duYW1lKSB7CiAgICAgIGNlbGxsaW5lID0gcGxvdERhdGFbcm93bmFtZSwgIkNlbGxMaW5lIl0KICAgICAgaXNvbGluZSA9IHBsb3REYXRhW3Jvd25hbWUsICJJc29MaW5lIl0KICAgICAgcmVwX3ZhbHVlcyA9IHBsb3REYXRhW3Bsb3REYXRhJENlbGxMaW5lID09IGNlbGxsaW5lLCAibG9nLlRQTSJdCiAgICAgIGlzb2xpbmVfdmFsdWVzID0gcGxvdERhdGFbcGxvdERhdGEkSXNvTGluZSA9PSBpc29saW5lLCAibG9nLlRQTSJdCiAgICAgIGhlaWdodCA9IG1pbihpc29saW5lX3ZhbHVlcykgKyAxLjEwICogKG1heChyZXBfdmFsdWVzKSAtIG1pbihpc29saW5lX3ZhbHVlcykpCiAgICAgIHJldHVybihoZWlnaHQpCiAgICB9KQogIAogICMgRm9yIHNldHRpbmcgeWxpbQogIHJhbmdlX3ZhbHVlcyA9IG1heChtYXgocGxvdERhdGEkbG9nLlRQTSksIG1heChwbG90RGF0YSRzaWdfaGVpZ2h0KSkgLSBtaW4ocGxvdERhdGEkbG9nLlRQTSkKICAKICBwcmludCgKICAgIGdncGxvdChwbG90RGF0YSwgYWVzKENlbGxMaW5lLCBsb2cuVFBNLCBjb2xvciA9IENlbGxMaW5lLCkpICsKICAgICAgZ2VvbV9iZWVzd2FybShhbHBoYSA9IDAuMywgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICAgICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BLCBmaWxsID0gTkEpICsKICAgICAgZ2VvbV90ZXh0KHkgPSBwbG90RGF0YSRzaWdfaGVpZ2h0LCBzaXplID0gMTAsIGxhYmVsID0gcGxvdERhdGEkc2lnbmlmaWNhbmNlKSArCiAgICAgIGdlb21fdGV4dCh5ID0gcGxvdERhdGEkbGFiZWxfaGVpZ2h0LCBzaXplID0gMiwgbGFiZWwgPSBwbG90RGF0YSRDZWxsTGluZSkgKwogICAgICB5bGltKG1pbihwbG90RGF0YSRsb2cuVFBNKSAtIDAuMSAqIHJhbmdlX3ZhbHVlcywgbWF4KHBsb3REYXRhJGxvZy5UUE0pICsgMC4xNSAqIHJhbmdlX3ZhbHVlcykgKyAKICAgICAgdGhlbWVfY2xhc3NpYygpICsKICAgICAgdGhlbWUoYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpKSArCiAgICAgIGdndGl0bGUodGl0bGUpKQp9CmBgYAoKCjwhLS0gYGBge3J9IC0tPgo8IS0tICMgVG8gcGxvdCB0aGVtIGFsbCB0b2dldGhlcjogLS0+Cgo8IS0tICMgIyBtYWtlIGxvbmcgdmVyc2lvbiBvZiB0YWJsZSAtLT4KPCEtLSAjIGxvbmcuZGF0YSA8LSBnYXRoZXIoVFBNLmxvZy5nZW5lcy5pbnRlcmVzdGluZywga2V5PSJHZW5lIiwgdmFsdWU9ImxvZ1RQTSIsIEFBUlMyOllBUlMyKSAtLT4KPCEtLSAjICAgICAgLS0+CjwhLS0gIyBwbG90IDwtIGdncGxvdChsb25nLmRhdGEsIGFlcyhDZWxsTGluZSwgbG9nVFBNLCBmaWxsID0gR2VuZSwgY29sb3IgPSBDZWxsTGluZSwpKSArIC0tPgo8IS0tICMgICAgICAgIyBnZW9tX2JlZXN3YXJtKGFscGhhID0gMC4zKSArIC0tPgo8IS0tICMgICAgICAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGUgPSBOQSwgZmlsbCA9IE5BKSArIC0tPgo8IS0tICMgICAgICAgdGhlbWVfY2xhc3NpYygpICsgLS0+CjwhLS0gIyAgICAgICB0aGVtZShheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLCAtLT4KPCEtLSAjICAgICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwgLS0+CjwhLS0gIyAgICAgICAgICAgICBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpKSArIC0tPgo8IS0tICMgICAgICAgZmFjZXRfd3JhcF9wYWdpbmF0ZSh2YXJzKEdlbmUpLCBucm93ID0gMSwgbmNvbCA9IDUpIC0tPgo8IS0tICMgIC0tPgo8IS0tICMgZm9yKGkgaW4gMTpuX3BhZ2VzKHBsb3QpKSAtLT4KPCEtLSAjICAgcHJpbnQoIC0tPgo8IS0tICMgICAgZ2dwbG90KGxvbmcuZGF0YSwgYWVzKENlbGxMaW5lLCBsb2dUUE0sIGZpbGwgPSBHZW5lLCBjb2xvciA9IENlbGxMaW5lLCkpICsgLS0+CjwhLS0gIyAgICAgICAjIGdlb21fYmVlc3dhcm0oYWxwaGEgPSAwLjMpICsgLS0+CjwhLS0gIyAgICAgICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BLCBmaWxsID0gTkEpICsgLS0+CjwhLS0gIyAgICAgICB0aGVtZV9jbGFzc2ljKCkgKyAtLT4KPCEtLSAjICAgICAgIHRoZW1lKGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksIC0tPgo8IS0tICMgICAgICAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLCAtLT4KPCEtLSAjICAgICAgICAgICAgIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpICsgLS0+CjwhLS0gIyAgICAgICBmYWNldF93cmFwX3BhZ2luYXRlKHZhcnMoR2VuZSksIG5yb3cgPSAxLCBuY29sID0gNSwgcGFnZSA9IGkpKSAtLT4KPCEtLSBgYGAgLS0+Cg==